package com.siwei.spatial.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

/**
 * 文件工具类
 */
public class FileUtils {

    /**
     * 解压ZIP文件，自动尝试UTF-8和GBK编码
     */
    public static void unzipAutoCharset(String zipFilePath, String destDir) throws IOException {
        try {
            unzipWithCharset(zipFilePath, destDir, Charset.forName("UTF-8"));
        } catch (ZipException e) { // 捕获ZIP结构或编码问题
            unzipWithCharset(zipFilePath, destDir, Charset.forName("GBK"));
        }
    }

    /**
     * 按指定字符集解压ZIP文件
     */
    private static void unzipWithCharset(String zipFilePath, String destDir, Charset charset) throws IOException {
        File dir = new File(destDir);
        if (!dir.exists() && !dir.mkdirs()) {
            throw new IOException("无法创建目录: " + destDir);
        }

        try (ZipFile zipFile = new ZipFile(zipFilePath, charset)) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                File newFile = safeNewFile(dir, entry.getName()); // 安全路径拼接

                if (entry.isDirectory()) {
                    if (!newFile.exists() && !newFile.mkdirs()) {
                        throw new IOException("无法创建目录: " + newFile);
                    }
                } else {
                    File parent = newFile.getParentFile();
                    if (!parent.exists() && !parent.mkdirs()) {
                        throw new IOException("无法创建文件目录: " + parent);
                    }
                    try (InputStream is = zipFile.getInputStream(entry);
                         FileOutputStream fos = new FileOutputStream(newFile)) {
                        byte[] buffer = new byte[4096];
                        int len;
                        while ((len = is.read(buffer)) > 0) {
                            fos.write(buffer, 0, len);
                        }
                    }
                }
            }
        }
    }

    /**
     * 防止Zip Slip漏洞：限制文件解压在目标目录内
     */
    private static File safeNewFile(File destinationDir, String entryName) throws IOException {
        File destFile = new File(destinationDir, entryName);
        String destDirPath = destinationDir.getCanonicalPath();
        String destFilePath = destFile.getCanonicalPath();
        if (!destFilePath.startsWith(destDirPath + File.separator)) {
            throw new IOException("非法ZIP条目: " + entryName);
        }
        return destFile;
    }

    /**
     * 读取文件夹中及子文件夹中的所有shp相关文件（.shp,.dbf,.prj,.qix,.shx），返回map结构，后缀名为key，文件路径为value
     *
     * @param dirPath 文件夹路径
     */
    public static List<Map<String, String>> readShpFiles(String dirPath) {
        File dir = new File(dirPath);
        if (!dir.exists() || !dir.isDirectory()) {
            throw new IllegalArgumentException("Provided path is not a valid directory: " + dirPath);
        }
        Map<String, Map<String, String>> groupedFiles = new HashMap<>();
        readShpFilesRecursively(dir, groupedFiles);
        return new ArrayList<>(groupedFiles.values());
    }

    /**
     * 递归读取目录中的shp相关文件，并将它们按文件名分组
     *
     * @param dir
     * @param groupedFiles
     */
    private static void readShpFilesRecursively(File dir, Map<String, Map<String, String>> groupedFiles) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    readShpFilesRecursively(file, groupedFiles);
                } else {
                    String fileName = file.getName();
                    String lowerName = fileName.toLowerCase();
                    if (lowerName.endsWith(".shp") || lowerName.endsWith(".dbf") || lowerName.endsWith(".prj") || lowerName.endsWith(".qix") || lowerName.endsWith(".shx")) {
                        String baseName = fileName.substring(0, fileName.lastIndexOf('.'));
                        String ext = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
                        groupedFiles.computeIfAbsent(baseName, k -> new HashMap<>()).put(ext, file.getAbsolutePath());
                    }
                }
            }
        }
    }
}